#!/usr/bin/env python
# -*-coding:utf-8 -*-

__all__ = ['CommunicationChannel', 'clientSendMessage', 'clientFetchMessage', 'serverSendMessage', 'serverFetchMessage']


from collections import deque
from random import random


class CommunicationChannel(object):
    """
    通信信道。
    """
    
    def __init__(self, timer, downlink_pkt_loss_rate=0.0, uplink_pkt_loss_rate=0.0):
        """
        创建一条通信信道。
        :param downlink_err_rate: 下行链路的丢包率。server到client的链路为下行链路。
        :param uplink_err_rate: 上行链路的丢包率。client到server的链路为上行链路。
        """
        self._timer = timer
        self._downlink_buf = deque()
        self._downlink_pkt_loss_rate = downlink_pkt_loss_rate
        self._uplink_buf = deque()
        self._uplink_pkt_loss_rate = uplink_pkt_loss_rate
    
    def clientSendMessage(self, msg):
        """
        客户端发送消息。
        :param msg: 发送的消息，为Message对象。
        """
        self._uplink_buf.append(msg)
        
    def clientFetchMessage(self, client_id):
        """
        客户端从信道接收消息。
        :param client_id: 客户端的ID。因为服务器发送的所有信息都会缓存在同一个缓冲区，通过client_id可以找到服务器发送给本客户的消息。
        :return: 接收的消息，为Message对象。返回缓冲区中第一个满足延时要求的消息，如果这样的消息不存在，返回None。
        """
        if len(self._downlink_buf) <= 0:
            return None
        for msg in self._downlink_buf:
            if msg.getReceiver() == client_id:
                if self._timer.getTime() - msg.getCreateTime() >= msg.getDelay():
                    self._downlink_buf.remove(msg)
                    if random() >= self._downlink_pkt_loss_rate:
                        return msg
        return None
    
    def serverSendMessage(self, msg):
        """
        服务器端发送消息。
        :param msg: 发送的消息，为Message对象。
        """
        self._downlink_buf.append(msg)
        
    def serverFetchMessage(self, client_id=None):
        """
        服务器端从信道接收来自客户端client_id的消息。
        :param client_id: 客户端的ID。因为客户端发送的所有信息都会缓存在同一个缓冲区，通过client_id可以找到来自相应客户端的消息。
        :return: 接收的消息，为Message对象。返回缓冲区中第一个满足延时要求的消息，如果这样的消息不存在，返回None。
        """
        if len(self._uplink_buf) <= 0:
            return None
        msg = self._uplink_buf[0]
        if client_id is None:
            if self._timer.getTime() - msg.getCreateTime() >= msg.getDelay():
                self._uplink_buf.popleft()
                if random() >= self._uplink_pkt_loss_rate:
                    return msg
        else:
            for msg in self._uplink_buf:
                if msg.getSender() == client_id:
                    if self._timer.getTime() - msg.getCreateTime() >= msg.getDelay():
                        self._uplink_buf.remove(msg)
                        if random() >= self._uplink_pkt_loss_rate:
                            return msg
                
        return None
        